﻿//Copyright (c) Microsoft Corporation.  All rights reserved.

using Microsoft.ApplicationServer.Caching;
using Microsoft.Ted.Wacel.Base;
using System;
using System.Collections.Generic;

namespace Microsoft.Ted.Wacel.Cache
{
    public class WindowsAzureCacheDataProvider: Base.IDataProvider
    {
        private DataCache mCache;
        private WindowsAzureCacheConfiguration mConfiguration;
        private string mRegion;
        public WindowsAzureCacheDataProvider(DataCache cache, string region)
        {
            if (cache == null)
                throw new ArgumentNullException("DataCache can't be null.");
            mRegion = region;
            mCache = cache;
        }
        public void Put(string key, object value, string partition = null)
        {
            Retrier.Invoke<string, object>(CacheRetryPolicy.DefaultPolicy, (k, v) =>
                {
                    if (mConfiguration.UseRegion)
                        mCache.Put(k, v, mRegion);
                    else
                        mCache.Put(mRegion + "." + k, v, new DataCacheTag[] { new DataCacheTag(mRegion) });
                }, key, value);
        }
        public void Update(string key, object newValue, string partition = null)
        {
            Retrier.Invoke<string, object>(CacheRetryPolicy.DefaultPolicy, (k, v) =>
                {
                    DataCacheLockHandle handle;
                    if (mConfiguration.UseRegion)
                    {
                        try
                        {
                            object oldObject = mCache.GetAndLock(k, TimeSpan.FromSeconds(2), out handle, mRegion);
                            mCache.PutAndUnlock(k, v, handle, mRegion);
                        }
                        catch (DataCacheException)
                        {
                            mCache.Add(k, v, mRegion);
                        }
                    }
                    else
                    {
                        try
                        {
                            object oldObject = mCache.GetAndLock(mRegion + "." + k, TimeSpan.FromSeconds(2), out handle);
                            mCache.PutAndUnlock(mRegion + "." + k, v, handle, mRegion);
                        }
                        catch (DataCacheException)
                        {
                            mCache.Add(k, v, mRegion);
                        }
                    }
                }, key, newValue);
        }
        public void PutInSub(string key, object value)
        {
            Put(key, value);
        }
        public object Get(string key, string partition = null, Type typeHint = null, Type typeHint2 = null)
        {
            return Retrier.Invoke<string, object>(CacheRetryPolicy.DefaultPolicy, (k) =>
                {
                    if (mConfiguration.UseRegion)
                        return mCache.Get(k, mRegion);
                    else
                        return mCache.Get(mRegion + "." + k);
                }, key);
        }

        public void BulkPut(IEnumerable<KeyValuePair<string, object>> values, string partition = null)
        {
            Retrier.Invoke<IEnumerable<KeyValuePair<string, object>>>(CacheRetryPolicy.DefaultPolicy, (v) =>
            {
                foreach (var i in v)
                {
                    if (mConfiguration.UseRegion)
                    {
                        mCache.Put(i.Key, i.Value, mRegion);
                    }
                    else
                    {
                        mCache.Put(mRegion + "." + i.Key, i.Value, new DataCacheTag[] { new DataCacheTag(mRegion) });
                    }
                }
            }, values);
        }

        public IEnumerable<KeyValuePair<string,object>> GetAll(string partition = null, Type typeHint = null, Type typeHint2 = null)
        {
            return Retrier.Invoke<IEnumerable<KeyValuePair<string, object>>>(CacheRetryPolicy.DefaultPolicy, () =>
               {
                   if (mConfiguration.UseRegion)
                       return mCache.GetObjectsInRegion(mRegion);
                   else
                       throw new NotImplementedException();
               });
        }
        public IEnumerable<KeyValuePair<string, object>> BulkGet(List<string> keys, string partition = null, Type typeHint = null, Type typeHint2 = null)
        {
            return Retrier.Invoke<List<string>, IEnumerable<KeyValuePair<string, object>>>(CacheRetryPolicy.DefaultPolicy, (k) =>
                {
                    if (mConfiguration.UseRegion)
                        return mCache.BulkGet(k, mRegion);
                    else
                    {
                        string[] altKeys = new string[k.Count];
                        for (int i = 0; i < k.Count; i++)
                            altKeys[i] = mRegion + "." + k[i];
                        return mCache.BulkGet(altKeys);
                    }
                }, keys);
        }

        public void Delete(string key, string partition = null)
        {
            Retrier.Invoke<string>(CacheRetryPolicy.DefaultPolicy, (k) =>
                {
                    if (mConfiguration.UseRegion)
                        mCache.Remove(key, mRegion);
                    else
                        mCache.Remove(mRegion + "." + key);
                }, key);
        }

        public void Clear()
        {
            Retrier.Invoke(CacheRetryPolicy.DefaultPolicy, () =>
                {
                    if (mConfiguration.UseRegion)
                        mCache.ClearRegion(mRegion);
                    else
                        throw new NotImplementedException();
                });
        }


        public Base.DataProviderConfiguration Configuration
        {
            get
            {
                return mConfiguration;
            }
            set
            {
                if (value is WindowsAzureCacheConfiguration)
                    mConfiguration = (WindowsAzureCacheConfiguration)value;
                else
                    throw new ArgumentException("Configuration is not a WindowsAzureCacheConfiguration type");
                mCache.CreateRegion(mRegion);
                //TODO: react on configuration changes
            }
        }
      
        public IDataProvider Clone(string newName)
        {
            return new WindowsAzureCacheDataProvider(mCache, newName) { Configuration = (WindowsAzureCacheConfiguration)mConfiguration.Clone() };
        }
    }
}
